#include "jlinkarm.h"
#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include "jlinkflashprogram.h"


void (*JLINKARM_SetLogFile)(const char *file);
u32 (*JLINKARM_GetDLLVersion)(void);
i32 (*JLINKARM_GetHardwareVersion)(void);
void (*JLINKARM_GetFirmwareString)(char *buffer, int BufferSize);
i32 (*JLINKARM_GetSN)(void);

i32 (*JLINKARM_ExecCommand)(const char *cmd, char *sError, int BufferSize);
i32 (*JLINKARM_TIF_Select)(int Interface);
void (*JLINKARM_SetSpeed)(uint32_t Speed);
u16 (*JLINKARM_GetSpeed)(void);
u32 (*JLINKARM_GetId)(void);
i32 (*JLINKARM_GetDeviceFamily)(void);
/**
 * return:
 * NULL ok
 * !=NULL Error string

*/
const char *(*JLINKARM_Open)(void);
void (*JLINKARM_Close)(void);
char (*JLINKARM_IsOpen)(void);
/**
 * >=0 ok
 * <0 Error
 */
int (*JLINKARM_Connect)(void);
/**
 * 1 connected
 * 0 not connected
 */
i8 (*JLINKARM_IsConnected)(void);
// i8       (*JLINKARM_EMU_IsConnected)(void);
// i8       (*JLINKARM_Halt)(void);
// i8       (*JLINKARM_IsHalted)(void);
// void     (*JLINKARM_SetResetType)(int reset_type);
i32 (*JLINKARM_Reset)(void);

// void  (*JLINK_Go)(void);
// void  (*JLINK_GoIntDis)(void);
u32 (*JLINKARM_ReadReg)(int RegIndex);
i8 (*JLINKARM_WriteReg)(int RegIndex, u32 data);

/**
 * 0 ok
 * 1 Error
 */
i32 (*JLINKARM_ReadMem)(u32 addr, u32 NumBytes, void *pData);
i32 (*JLINKARM_ReadMemHW)(u32 addr, u32 NumBytes, void *pData);

/**
 * >=0 ok
 * <0 Error
 */
int (*JLINKARM_WriteMem)(u32 addr, u32 count, void* data);
/**
 * =0 ok
 * !=0 Error
 */
int (*JLINK_WriteU8)(u32 addr, u16 data);
int (*JLINK_WriteU16)(u32 addr, u16 data);
int (*JLINK_WriteU32)(u32 addr, u32 data);

/**
 * >=0 ok
 * <0 Error
 */
int (*JLINK_EraseChip)(void);
/**
 * Flags: Reserved for future use. Set to 0.
 */
void (*JLINKARM_BeginDownload)(u32 Flags);
/**
 * This function programs a given data file to a specified destination address.
Currently supported data files are:
? *.mot
? *.srec
? *.s19
? *.s
? *.hex
? *.bin
return:
 * >=0 ok
 * <0 Error
*/
int (*JLINK_DownloadFile)(const char *sFileName, u32 addr);
/**
 * >=0 Numberofprogrammedbytes.
Might be different from the num-
berofbytesinitiallywrittenvia
multiple calls of JLINKARM_Write-Mem() since the DLL optimizes
flash download and only programsthenecessaryranges.If
somerangesarealreadyprogrammed with the correct data,programmingfortheserangesis skipped.
  <0 Error
*/
int (*JLINKARM_EndDownload)(void);

/**
 * >=0 ok
 * <0 Error

*/
int  (*JLINKARM_EMU_SelectByUSBSN)(U32 SerialNo);
/**
 * 
 * port: USB port to be used for the connection. Valid values range from 0 to 3
 * return:
 * 0 ok
 * 1 Error
*/
int  (*JLINKARM_SelectUSB)(int port);
/**
 * 0 ok
 * 1 Error
*/
int  (*JLINKARM_SelectIP)(const char* sHost, int Port);

void (*JLINKARM_Go)(void);



const char* (*JLINKARM_OpenEx)(JLINKARM_LOG* pfLog, JLINKARM_LOG* pfErrorOut);



void LoadJLinkFunctions()
{
    HMODULE hModule = LoadLibrary("JLinkARM.dll");
    if (hModule != NULL)
    {
        JLINKARM_SetLogFile = (void *)GetProcAddress(hModule, "JLINKARM_SetLogFile");
        JLINKARM_GetDLLVersion = (void *)GetProcAddress(hModule, "JLINKARM_GetDLLVersion");
        JLINKARM_GetHardwareVersion = (void *)GetProcAddress(hModule, "JLINKARM_GetHardwareVersion");
        JLINKARM_GetFirmwareString = (void *)GetProcAddress(hModule, "JLINKARM_GetFirmwareString");
        JLINKARM_GetSN = (void *)GetProcAddress(hModule, "JLINKARM_GetSN");
        JLINKARM_ExecCommand = (void *)GetProcAddress(hModule, "JLINKARM_ExecCommand");
        JLINKARM_TIF_Select = (void *)GetProcAddress(hModule, "JLINKARM_TIF_Select");
        JLINKARM_SetSpeed = (void *)GetProcAddress(hModule, "JLINKARM_SetSpeed");
        JLINKARM_GetSpeed = (void *)GetProcAddress(hModule, "JLINKARM_GetSpeed");
        JLINKARM_GetId = (void *)GetProcAddress(hModule, "JLINKARM_GetId");
        JLINKARM_GetDeviceFamily = (void *)GetProcAddress(hModule, "JLINKARM_GetDeviceFamily");
        JLINKARM_Open = (void *)GetProcAddress(hModule, "JLINKARM_Open");
        JLINKARM_Close = (void *)GetProcAddress(hModule, "JLINKARM_Close");
        JLINKARM_IsOpen = (void *)GetProcAddress(hModule, "JLINKARM_IsOpen");
        JLINKARM_Connect = (void *)GetProcAddress(hModule, "JLINKARM_Connect");
        JLINKARM_IsConnected = (void *)GetProcAddress(hModule, "JLINKARM_IsConnected");
        JLINKARM_Reset = (void *)GetProcAddress(hModule, "JLINKARM_Reset");
        JLINKARM_ReadReg = (void *)GetProcAddress(hModule, "JLINKARM_ReadReg");
        JLINKARM_WriteReg = (void *)GetProcAddress(hModule, "JLINKARM_WriteReg");
        JLINKARM_ReadMem = (void *)GetProcAddress(hModule, "JLINKARM_ReadMem");
        JLINKARM_ReadMemHW = (void *)GetProcAddress(hModule, "JLINKARM_ReadMemHW");
        JLINKARM_WriteMem = (void *)GetProcAddress(hModule, "JLINKARM_WriteMem");
        JLINK_WriteU8 = (void *)GetProcAddress(hModule, "JLINK_WriteU8");
        JLINK_WriteU16 = (void *)GetProcAddress(hModule, "JLINK_WriteU16");
        JLINK_WriteU32 = (void *)GetProcAddress(hModule, "JLINK_WriteU32");
        JLINK_EraseChip = (void *)GetProcAddress(hModule, "JLINK_EraseChip");
        JLINKARM_BeginDownload = (void *)GetProcAddress(hModule, "JLINKARM_BeginDownload");
        JLINK_DownloadFile = (void *)GetProcAddress(hModule, "JLINK_DownloadFile");//jlink not jlinkarm
        JLINKARM_EndDownload = (void *)GetProcAddress(hModule, "JLINK_EndDownload");
        JLINKARM_OpenEx = (void *)GetProcAddress(hModule, "JLINKARM_OpenEx");
        JLINKARM_EMU_SelectByUSBSN = (void *)GetProcAddress(hModule, "JLINKARM_EMU_SelectByUSBSN");
        JLINKARM_SelectUSB = (void *)GetProcAddress(hModule, "JLINKARM_SelectUSB");
        JLINKARM_SelectIP = (void *)GetProcAddress(hModule, "JLINKARM_SelectIP");
        JLINKARM_Go = (void *)GetProcAddress(hModule, "JLINKARM_Go");       
        
        // = (void*)GetProcAddress(hModule, "");
    }
}

/*********************************************************************
 *
 * _cbLogOutHandler
 *
 * Function description
 * Call-back function used to ouput log messages from the J-Link DLL
 */
static void _cbLogOutHandler(const char *sLog)
{
    logI("Log: %s", sLog);
}
/*********************************************************************
 *
 * _cbErrorOutHandler
 *
 * Funcction description
 * Call-back function used to ouput error messages from the J-Link DLL
 */
static void _cbErrorOutHandler(const char *sError)
{
    logE("Error: %s", sError);
}

/*********************************************************************
*
* _InitDebugSession
*
* Function description
* Initializes the debug session by connecting to a J-Link,
* setting up the J-Link DLL and J-Link and finally connecting to the target
 system
*
* Return value
* 0 O.K.
* < 0 Error
*/
int _InitDebugSession(INIT_PARAS *pParas)
{
    const char *sError;
    U8 acIn[0x400];
    U8 acOut[0x400];
    int r;
   
    //
    // Select and configure host interface (USB is default)
    //
    if (pParas->HostIF == 1)
    { // Host interface == IP?
        //
        // If sHost is NULL, J-Link selection dialog will pop-up
        // on JLINKARM_Open() / JLINKARM_OpenEx(), showing all
        // emulators that have been found in the network
        // Passing 0 as port selects the default port (19020).
        //
        r = JLINKARM_SelectIP(pParas->sHost, 0);
        if (r == 1)
        {
            return -1; // Error occurred during configuration of IP interface
        }
    }
    else
    { // Connect via USB
        //
        // Was a specific serial number set we shall to connect to?
        //
        if (pParas->SerialNo)
        {
            r = JLINKARM_EMU_SelectByUSBSN(pParas->SerialNo);
            if (r < 0)
            {
                return -1; // Error: Specific serial number not found on USB
            }
        }
    }
    //
    // Open communication with J-Link
    //
    sError = JLINKARM_OpenEx(_cbLogOutHandler, _cbErrorOutHandler);
    if (sError)
    { // Error occurred while connecting to J-Link?
        logE("Error: %s", sError);
        return -1;
    }
    //
    // Select settings file
    // Used by the control panel to store its settings and can be used by the user to
    // enable features like flash breakpoints in external CFI flash, if not selectable
    // by the debugger. There should be different settings files for different debug
    // configurations, for example different settings files for LEDSample_DebugFlash
    // and LEDSample_DebugRAM. If this file does not exist, the DLL will create one
    // with default settings. If the file is already present, it will be used to load
    // the control panel settings
    //
    if (pParas->sSettingsFile)
    {
        strcpy(acIn, "ProjectFile = ");
        strcat(acIn, pParas->sSettingsFile);
        JLINKARM_ExecCommand(acIn, acOut, sizeof(acOut));
        if (acOut[0])
        {
            logE("Error: %s", acOut);
            return -1;
        }
    }
    //
    // Select device or core
    //
    if (pParas->sDevice)
    {
        strcpy(acIn, "device = ");
        strcat(acIn, pParas->sDevice);
        JLINKARM_ExecCommand(acIn, &acOut[0], sizeof(acOut));
        if (acOut[0])
        {
            logE("Error: %s",acOut);
            return -1;
        }
    }
    //
    // For some targets a special connect / reset etc. may be needed
    // which can be implemented via a J-Link script file
    // J-Link script files should be passed to the DLL right after selecting the device
    //
    if (pParas->sScriptFile)
    {
        strcpy(acIn, "ScriptFile = ");
        strcat(acIn, pParas->sScriptFile);
        JLINKARM_ExecCommand(acIn, NULL, 0);
    }
    //
    // Select and configure target interface
    // If not called, J-Link will use the interface which was configured before. If
    // J-Link is power-cycled, JTAG is the default target interface.
    // It is recommended to always select the interface at debug startup.
    //
    JLINKARM_TIF_Select(pParas->TargetIF);
    //
    // Select target interface speed which
    // should be used by J-Link for target communication
    //
    JLINKARM_SetSpeed(pParas->Speed);
    //
    // Connect to target CPU
    //
    r = JLINKARM_Connect();
    if (r)
    {
        logE("Could not connect to target.");
        return -1;
    }

    return 0;
}



/*********************************************************************
 *
 * main
 */
int main(int argc, char *argv[], char *envp[])
{
    INIT_PARAS Paras;
    int r;
    
    LoadJLinkFunctions();
    //
    // Zero-initialize structure to make sure that default values are configured
    // for fields that are not used. In this sample we use a simple connect via USB,
    // so no need to setup Paras.HostIF and Paras.SerialNo
    //
    memset(&Paras, 0, sizeof(INIT_PARAS));
    Paras.TargetIF = JLINKARM_TIF_SWD;
    //
    // Target interface speed in kHz
    // Also possible: JLINKARM_SPEED_AUTO, JLINKARM_SPEED_ADAPTIVE
    //
    Paras.Speed = 1000;
    Paras.sSettingsFile = "settings.jlink";
    //
    // Select device: For a list of valid values see:
    // http://www.segger.com/jlink_supported_devices.html
    //
    Paras.sDevice = "HC32F460XE";
    //
    // No special JLink script file needed for this device
    //
    Paras.sScriptFile = NULL;
    logI("Initializing debugger...");
    r = _InitDebugSession(&Paras);
    if (r < 0)
    {
        logE("Initialization of debug session failed.");
        return -1;
    }
    logI("Debugger initialized successfully.");
    //
    // From now on the target is identified and we can start working with it.
    //
    // ...
    //
    // Close connection to J-Link
    //
    //program_bin_file("boot.bin", 0x0);

    //arg [addr] just for file which no address information(eg. "*.bin",) 
    program_erase_chip();
    program_file("boot.hex", 0x80000);
    JLINKARM_Close();
    return 0;
}

////////////////////////////////////////////////
#if 0 // Example of readReg
void ReadWriteTest(void) {
 U32 v;
 //
 // Write register R0
 //
 JLINKARM_WriteReg(ARM_REG_R0, 0xFF);
 //
 // Read register R0
 //
 v = JLINKARM_ReadReg(ARM_REG_R0);
 if(v != 0xFF) {
 logE("Error! R0 Value = %i ", v);
 } else {
 logI("ARM_REG_R0: %i", v);
 }
}
void ReadWriteCycleCount(U32 CycleValue) {
 U32 v1, v2;
 JLINKARM_Halt();
 //
 // Read current cylce count
 //
 v1 = JLINKARM_ReadReg(JLINKARM_CM3_CYCLECNT);//Change define to match the used core
 logI("Current Cycle Count: %i ", v1);
 JLINKARM_Go();
 sleep(10);
 JLINKARM_Halt();
 //
 // Read new cycle count and calculate cycles since last Go()
 //
 v2 = JLINKARM_ReadReg(JLINKARM_CM3_CYCLECNT);
 logI("Current Cycle Count: %i , v2);
 logI("Cycles since last Go(): %i ", v2-v1);
}
#endif

// Example of JLINKARM_ExecCommand
#if 0
char acCmd[256];
int r;
strcpy(acCmd, "Device = AT91SAM7S256");
//
// Execute command "Device"
//
r = JLINKARM_ExecCommand(acCmd, NULL, 0);
logI("Return value of command: %d", r);
char acCmd[256];
char acOut[256];
int r;
strcpy(acCmd, "Device = AT91SAM7S256");
//
// Execute command "Device"
//
r = JLINKARM_ExecCommand(acCmd, acOut, sizeof(acOut));
//
// Check if command executed successfully
//
if (acOut[0] == 0) {
 logI("Command executed successfully!");
 logI("Return value of command: %d", r);
} else {
 logE("Failed to execute command!");
 logE("Error string: %s", acOut);
}
#endif

// Example of JLINKARM_BeginDownload and JLINKARM_EndDownload
#if 0
Examples:
char acBuffer[10] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A};
JLINKARM_BeginDownload(0); // Indicates start of flash download
//
// The following 10 bytes are written into the flash download buffer of the DLL
//
JLINKARM_WriteMem(FLASH_START_ADDR, 5, &acBuffer[0]);
JLINKARM_WriteMem(FLASH_START_ADDR + 5, 5, &acBuffer[5]);
JLINKARM_EndDownload(); // Indicates end of flash download.
 // DLL will download all data into flash memory
#endif